#include "stdafx.h"

Engine::Engine(void)
{
    // Window Event Handlers
    smWindowKillFocus = gcnew EventHandler(&Engine::WindowKillFocus);
    smWindowMinimized = gcnew EventHandler(&Engine::WindowMinimized);
    smWindowRestored = gcnew EventHandler(&Engine::WindowRestored);
    smWindowSetFocus = gcnew EventHandler(&Engine::WindowSetFocus);
}


Renderer^ Engine::GetRenderer(void)
{
    return smRenderer;
}


World^ Engine::GetWorld(void)
{
    return smWorld;
}


void Engine::Initialize(Window^ argWindow)
{
    if (nullptr == argWindow)
        throw gcnew ArgumentNullException("argWindow");

    smMainWindow = argWindow;

    // Initialize Engine Thread
    {
        smLogger->Info("Initiailzing Engine Thread.");
        smEngineThread = gcnew Thread(gcnew ThreadStart(&Engine::Run));
    }

    // Initialize Timer
    {
        smLogger->Info("Initializing Timer");
        smTimer = gcnew Timer(SecondsPerTick);
    }

    // Initialize the input manager
    {

        smLogger->Info("Initializing Input.");
        smInput = gcnew Input();
        smInput->Acquire(smMainWindow);
    }

    // Initialize the window
    {
        smMainWindow->KillFocus += smWindowKillFocus;
        smMainWindow->Minimized += smWindowMinimized;
        smMainWindow->Restored += smWindowRestored;
        smMainWindow->SetFocus += smWindowSetFocus;
        smMainWindow->Sized += smWindowSized;
    }

    // Initialize the Renderer
    {
        smLogger->Info("Initializing Renderer.");
        smRenderer = gcnew Renderer(smMainWindow);
    }

    // Initialize Views
    {
        smWorldView = Coordinates::Rectangle(0, 0, 512, 512);
        smUIView = Coordinates::Rectangle(0, 0, 512, 512);
        smLogger->Info("Initializing World View: {0}", smWorldView);
        smLogger->Info("Initializing UI View: {0}", smUIView);
    }

    // Initialize Loading Bar
    {
        smLogger->Info("Initializing Loading Bar.");
        smLoadingBar = gcnew LoadingBar();
        smLoadingBar->Location = Coordinates::Location(128.0f, 400.0f);
    }

    // Initialize Load Thread
    {
        smLogger->Info("Initiailzing Load Thread.");
        smLoadThread = gcnew Thread(gcnew ThreadStart(&Engine::Load));
    }

}


void Engine::Start(void)
{
    if (!smRunning)
    {
        if (nullptr == smMainWindow)
            throw gcnew Exception("The Engine has not been initialized with a window");

        // Start the Engine thread
        {
            smLogger->Info("Starting Engine Thread.");

            // Set state to running
            smRunning = true;
            smEngineThread->Start();
        }

        // Start the Loading Thread
        {
            smLogger->Info("Starting Load Thread.");

            // Set state to running
            smLoading = true;
            smLoadThread->Start();
        }
    }
}


void Engine::Stop(void)
{
    if (smRunning)
    {
        // Set state to not running
        {
            smLogger->Info("Stopping Engine.");

            // Join the render thread
            smRunning = false;
            smEngineThread->Join();
        }

        // Stop the renderer
        {
            smLogger->Info("Stopping renderer.");
            delete smRenderer;
            smRenderer = nullptr;
        }

        // Initialize the window
        {
            smMainWindow->KillFocus -= smWindowKillFocus;
            smMainWindow->Minimized -= smWindowMinimized;
            smMainWindow->Restored -= smWindowRestored;
            smMainWindow->SetFocus -= smWindowSetFocus;
            smMainWindow->Sized -= smWindowSized;
        }

        // Unacquire the input
        {
            smLogger->Info("Unacquiring input.");
            smInput->Unacquire();
        }
    }
}


void Engine::Run(void)
{
    while (smRunning)
    {
        if (smStallRendering)
        {
            continue;
        }

        // Start timing
        smTimer->BeginTiming();

        // See if we have recieved a tick
        if (smTimer->Tick())
        {
            // Clear Renderer to black
            smRenderer->Clear();

            if (!smLoading)
            {
                // Update
                Update();

                // Render
                Render(/*%spriteList*/);
            } 
            else
            {
                // Update Loading Bar
                smLoadingBar->Update();

                // Draw Loading Bar
                smRenderer->SetView(smUIView);
                {
                    List<Sprite^> spriteList;
                    //spriteList.Clear();
                    smRenderer->BeginDrawing();
                    smLoadingBar->GetSprites(%spriteList);
                    smRenderer->Draw(%spriteList);
                    smRenderer->EndDrawing();
                }
            }

            // Reset the timer
            smTimer->Reset();

        }

        // Present this frame
        smRenderer->Present();

        // End timing this frame
        smTimer->EndTiming();
    }

    ResourceManager::ReleaseTextures();
}


void Engine::Load(void)
{
    smLoadingBar->SetValue(0.1f);

    // Initialize ResourceManager
    {
        smLogger->Info("Initializing Resource Manager.");
        ResourceManager::Initialize();
    }

    smLoadingBar->SetValue(0.2f);

    // Initialize Scripting Engine
    {
        smLogger->Info("Initiailzing Scripting Engine.");
        ScriptingEngine::Initialize();
    }

    smLoadingBar->SetValue(0.3f);

    // Load Objects
    {
        ResourceManager::LoadObjects("data/monster/Monsters.xml");
    }

    smLoadingBar->SetValue(0.4f);

    // Load User Interface
    {
        smLogger->Info("Initializing User Interface.");
        smUserInterface = gcnew UI();
    }

    smLoadingBar->SetValue(0.5f);

    // Load World
    {
        smLogger->Info("Initializing World.");
        smWorld = World::Load("data/world/Garden.xml");
    }

    smLoadingBar->SetValue(0.8f);

    // Load Player
    {
        smLogger->Info("Initializing Player.");
        smPlayer = gcnew Player();
        smPlayer->Location = Coordinates::Location(512/2, 512/2);
    }

    smLoadingBar->SetValue(0.9f);

    // Load Smoke Particle System
    {
        smSmokeParticleSystem = gcnew ParticleSystem<SmokeParticle^>(SmokeParticle::smDefaultSettings);
        smSmokeParticleSystem->Origin = Coordinates::Location(256, 256);
        smSmokeParticleSystem->Regenerate = true;
    }

    // Load Fire Particle System
    {
        smFireParticleSystem = gcnew ParticleSystem<FireParticle^>(FireParticle::smDefaultSettings);
        smFireParticleSystem->Origin = Coordinates::Location(384, 256);
        smFireParticleSystem->Regenerate = true;
    }

    // Load Aura Particle System
    {
        smAuraParticleSystem = gcnew ParticleSystem<AuraParticle^>(AuraParticle::smDefaultSettings);
        smAuraParticleSystem->Origin = Coordinates::Location(smPlayer->Location.X + (smPlayer->Size.Width / 2.0f) - 8, smPlayer->Location.Y + (smPlayer->Size.Height / 2.0f));
        smAuraParticleSystem->Regenerate = true;
    }

    smLoadingBar->SetValue(1.0f);

    // Temporary for now
    smAuraParticleSystem->Begin();
    smFireParticleSystem->Begin();
    smSmokeParticleSystem->Begin();

    smLogger->Info("Loading Thread exiting.");

    // Finish Loading
    smLoading = false;

}


void Engine::Update(void)
{
    // Process input 
    {
        // Update our key states
        smInput->UpdateKeyStates();

        // For now, escape key exits
        if (smInput->KeyDownState[DIK_ESCAPE])
        {
            QuitFlag = true;
        }

        // Reset Actions
        smPlayer->Actions.MoveLeft = false;
        smPlayer->Actions.MoveRight = false;
        smPlayer->Actions.MoveUp = false;
        smPlayer->Actions.MoveDown = false;
        smPlayer->Actions.Attack = false;

        // Hardcoded key values for now
        if (smInput->KeyPressedState[DIK_LEFT])
        {
            smPlayer->Actions.MoveLeft = true;
        }
        else if (smInput->KeyPressedState[DIK_RIGHT])
        {
            smPlayer->Actions.MoveRight = true;
        }

        if (smInput->KeyPressedState[DIK_UP])
        {
            smPlayer->Actions.MoveUp = true;
        }
        else if (smInput->KeyPressedState[DIK_DOWN])
        {
            smPlayer->Actions.MoveDown = true;
        }

        // Test the health and magic bars
        static float health = 0;
        if (smInput->KeyPressedState[DIK_Q])
        {
            smUserInterface->HealthBar->SetValue(health += 0.01f);
        }
        else if (smInput->KeyPressedState[DIK_W])
        {
            smUserInterface->HealthBar->SetValue(health -= 0.01f);
        }

        // Test the health and magic bars
        static float magic = 0;
        if (smInput->KeyPressedState[DIK_A])
        {
            smUserInterface->MagicBar->SetValue(magic += 0.01f);
        }
        else if (smInput->KeyPressedState[DIK_S])
        {
            smUserInterface->MagicBar->SetValue(magic -= 0.01f);
        }
    }

    // Update World
    {
        // Do nothing right now
    }

    // Update Player
    {
        smPlayer->Update();
    }

    // Update User Interface
    {
        smUserInterface->Update();
    }

    // Update Particle System(s)
    {
        // Move Aura with Player
        smAuraParticleSystem->Origin = Coordinates::Location(smPlayer->Location.X + (smPlayer->Size.Width / 2.0f) - 8, smPlayer->Location.Y + (smPlayer->Size.Height / 2.0f));

        smAuraParticleSystem->Update();
        smSmokeParticleSystem->Update();
        smFireParticleSystem->Update();
    }

    // Update World View
    {
        if (smPlayer->Location.X + (smWorldView.Width / 2) > smWorld->Size.Width) 
        {
            smWorldView.X = smWorld->Size.Width - smWorldView.Width;
        }
        else if (smPlayer->Location.X - (smWorldView.Width / 2) > 0)
        {
            smWorldView.X = smPlayer->Location.X - (smWorldView.Width / 2);
        }
        else
        {
            smWorldView.X = 0;
        }

        if (smPlayer->Location.Y + (smWorldView.Height / 2) > smWorld->Size.Height)
        {
            smWorldView.Y = smWorld->Size.Height - smWorldView.Height;
        }
        else if (smPlayer->Location.Y - (smWorldView.Height / 2) > 0)
        {
            smWorldView.Y = smPlayer->Location.Y - (smWorldView.Height / 2);
        }
        else 
        {
            smWorldView.Y = 0;
        }
    }

}


void Engine::Render(void /*List<Sprite^>^ argSpriteList*/)
{
    // Begin Drawing
    {
        smRenderer->BeginDrawing();
    }

    // Draw the sprites in the world
    smRenderer->SetView(smWorldView);
    {
        List<Sprite^> worldSprites;

        // Draw Background Layer
        {
            // Get Layer WorldObjects in WorldView Rectangle
            List<WorldObject^>^ objectList = smWorld->Layers["Background"]->GetObjects(smWorldView);
            for each (WorldObject^ object in objectList)
                object->GetSprites(%worldSprites);
        }

        // Draw Objects in World
        {
            // Get World Objects in WorldView Rectangle
            List<WorldObject^>^ objectList = smWorld->GetObjects(smWorldView);
            for each (WorldObject^ object in objectList)
                object->GetSprites(%worldSprites);
        }

        // Draw Player
         
        {
            smPlayer->GetSprites(%worldSprites);
        }

        // Draw Particle System(s)
        {
            smSmokeParticleSystem->GetSprites(%worldSprites);
            smFireParticleSystem->GetSprites(%worldSprites);
            smAuraParticleSystem->GetSprites(%worldSprites);
        }

        // Draw Foreground Layer
        {

        }

        // Draw Sprite List
        smRenderer->Draw(%worldSprites);
    }

    // Draw UI
    smRenderer->SetView(smUIView);
    {
        List<Sprite^> uiSprites;
        smUserInterface->GetSprites(%uiSprites);
        smRenderer->Draw(%uiSprites);
    }

    // End Drawing
    {
        smRenderer->EndDrawing();
    }
}


void Engine::WindowKillFocus(Object^ argSender, EventArgs^ e)
{
    smInput->Unacquire();
    smLogger->Info("KillFocus, unacquire input");
}


void Engine::WindowMinimized(Object^ argSender, EventArgs^ e)
{
    smStallRendering = true;
}


void Engine::WindowRestored(Object^ argSender, EventArgs^ e)
{
    smStallRendering = false;
}


void Engine::WindowSetFocus(Object^ argSender, EventArgs^ e)
{
    smInput->Acquire(smMainWindow);
    smLogger->Info("SetFocus, acquire input");
}